﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
using System.IO.Ports;
using System.IO;
using System.Threading;

namespace WindowsFormsApp1
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        ///* 析构函数 */
        //~Form1()
        //{
        //    //getReceive.Abort(); //关闭线程
        //    button2.Text = "打开串口";
        //}

        private Thread getReceive;
        protected Boolean stop = false;
        protected Boolean conState = false;
        private StreamReader sRead;
        string strReceive;
        bool bAccpet = false;

        SerialPort sp = new SerialPort();   //实例化串口通信类

        //以下定义4个公有变量，用于参数传递
        public static string strPortName = "";
        public static string strBaudRate = "";
        public static string strDataBits = "";
        public static string strStopBits = "";

        private void Form1_Load(object sender, EventArgs e)
        {
            this.toolStripStatusLabel1.Text = "端口号：端口未打开  | ";
            this.toolStripStatusLabel2.Text = "波特率：端口未打开  | ";
            this.toolStripStatusLabel3.Text = "数据位：端口未打开  | ";
            this.toolStripStatusLabel4.Text = "停止位：端口未打开  | ";
            this.toolStripStatusLabel5.Text = "";
        }

        /* 设置串口 */
        private void button1_Click(object sender, EventArgs e)
        {
            timer1.Enabled = false;
            sp.Close();
            Form2 f2 = new Form2();
            if (f2.ShowDialog() == DialogResult.OK)
            {
                sp.PortName = strPortName;
                sp.BaudRate = int.Parse(strBaudRate);
                sp.DataBits = int.Parse(strDataBits);
                sp.StopBits = (StopBits)int.Parse(strStopBits);
                sp.ReadTimeout = 500;   //读串口数据超时，引发ReadExisting异常
            }
        }

        /* 打开关闭串口 */
        private void button2_Click(object sender, EventArgs e)
        {
            if (button2.Text == "打开串口")
            {
                if (strPortName != "" && strBaudRate != "" && strDataBits != "" && strStopBits != "")
                {
                    try
                    {
                        if (sp.IsOpen)
                        {
                            sp.Close();
                            sp.Open();
                        }
                        else
                        {
                            sp.Open();
                        }
                        button2.Text = "关闭串口";

                        this.toolStripStatusLabel1.Text = "端口号：" + sp.PortName + "  | ";
                        this.toolStripStatusLabel2.Text = "波特率：" + sp.BaudRate + "  | ";
                        this.toolStripStatusLabel3.Text = "数据位：" + sp.DataBits + "  | ";
                        this.toolStripStatusLabel4.Text = "停止位：" + sp.StopBits + "  | ";


                        sp.Encoding = Encoding.GetEncoding("GB2312");

                        /* 打开串口后，创建接收线程 */
                        getReceive = new Thread(new ThreadStart(testDelegate));
                        getReceive.Start();



                    }
                    catch (Exception ex)
                    {
                        MessageBox.Show("错误：" + ex.Message, "C#串口通信");  //.NET异常机制就是屌
                    }

                }
            }
            else
            {
                button2.Text = "打开串口";
                if (sp.IsOpen)
                    sp.Close();
                this.toolStripStatusLabel1.Text = "端口号：端口未打开  | ";
                this.toolStripStatusLabel2.Text = "波特率：端口未打开  | ";
                this.toolStripStatusLabel3.Text = "数据位：端口未打开  | ";
                this.toolStripStatusLabel4.Text = "停止位：端口未打开  | ";


                try
                {
                    if (null != getReceive)
                    {
                        if (getReceive.Join(100))
                        {
                            getReceive.Abort(); //关闭线程
                        }
                    }
                    getReceive = null;
                }
                catch (Exception)
                {

                }

            } 
        }

        /* 发送数据 */
        private void button4_Click(object sender, EventArgs e)
        {
            if (sp.IsOpen)
            {
                try
                {
                    sp.Encoding = System.Text.Encoding.GetEncoding("GB2312");
                    sp.Write(richTextBox1.Text);    //发送数据
                }
                catch (Exception ex)
                {
                    MessageBox.Show("错误：" + ex.Message);
                }

                
            }
        }

        /* 定义一个委托类型，这个类型无参数无返回值 */      /* 可以把委托想象成一个类，该类中只有函数的实现，而没有包含成员变量 */
        delegate void reaction();

        private void testDelegate()
        {
            //reaction r = new reaction(fun);
            //r();
            //fun();
            while (button2.Text == "关闭串口" && this.Visible)
            {
                AcceptData();
            }
        }



        void fun()
        {
            //while (button2.Text == "关闭串口")  
            //{
            //    AcceptData();
            //}
        }

        //下面用到了接收信息的代理功能，此为设计的要点之一
        /* 定义一个委托类型，这个类型无参数无返回值 */      /* 可以把委托想象成一个类，该类中只有函数的实现，而没有包含成员变量 */
        /* 委托的地位相当于类 */
        /* 当实例化一个委托时，这些实例都要包含一个方法名，并且这个方法必须无参数无返回值（和委托类型一样） */
        delegate void DelegateAcceptData();
        //配合https://www.cnblogs.com/vaevvaev/p/6909042.html理解
        void AcceptData()
        {
            if (richTextBox2.InvokeRequired)    //InvokeRequired 当前线程不是创建控件的线程时为true
            {
                try
                {
                    DelegateAcceptData add = new DelegateAcceptData(AcceptData);
                    this.Invoke(add, new object[] { }); //invoke函数会顺着控件树向上搜索，知道找到创建这个控件的线程（通常是主线程），然后进入那个线程改变控件的外观，这样就不会发生线程冲突。
                }
                catch (Exception)
                {

                }
            }
            else
            {   //到这的时候，说明已经通过Invoke函数到达了创建richTextBox2控件的线程
                try
                {
                    strReceive = sp.ReadExisting();
                    richTextBox2.AppendText(strReceive);    //控件richTextBox2是由主线程创建的，而这里调用它的getReceive线程，这样会出错（原因可能是：在这里改变控件的状态，可能会与主线程发生冲突，因为主线程可能也在绘制这个控件）,调用invoke函数可以解决这个问题
                }
                catch (Exception)
                {

                }
            }
        }

        /* 当窗体关闭时触发 */ /* 不过貌似没触发。。。 */
        private void Form1_FormClosed(object sender, FormClosingEventArgs e)
        {
            //this.Dispose(); /* 释放当前窗体资源 */
            //this.Close();   /* 关闭窗口 */
            //System.Environment.Exit(0); /* 强行关闭所有线程进而退出进程 */
        }

        /* 浏览文件 */
        private void button5_Click(object sender, EventArgs e)
        {
            OpenFileDialog open = new OpenFileDialog();
            open.InitialDirectory = "D\\";
            open.RestoreDirectory = true;
            open.FilterIndex = 1;
            open.Filter = "文本文件(*.txt)|*.txt|所有文件(*.*)|*.*";
            if (open.ShowDialog() == DialogResult.OK)
            {
                try
                {
                    if (open.OpenFile() != null)
                    {
                        textBox1.Text = open.FileName;
                    }
                }
                catch (Exception err1)
                {
                    MessageBox.Show("文件打开错误！" + err1.Message, "提示信息", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                }
            }
        }

        /* 发送文件内容 */
        private void button6_Click(object sender, EventArgs e)
        {
            string fileName = textBox1.Text.Trim();
            if (fileName == "")
            {
                MessageBox.Show("请选择要发送的文件！", "Error");
                return;
            }
            else
            {
                sRead = new StreamReader(fileName, Encoding.Default);   //解决中文乱码问题
            }
            timer1.Start();
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            string str1;
            str1 = sRead.ReadLine();
            if (str1 == null)
            {
                timer1.Stop();
                sRead.Close();
                MessageBox.Show("文件发送成功！", "C#串口通信");
                this.toolStripStatusLabel5.Text = "";
                return;
            }
            byte[] data = Encoding.Default.GetBytes(str1);
            sp.Write(data, 0, data.Length);
            this.toolStripStatusLabel5.Text = "文件发送中...";
        }

        private void button3_Click(object sender, EventArgs e)
        {
            try
            {
                string path = Directory.GetCurrentDirectory() + @"\output.txt";
                string content = this.richTextBox2.Text;
                FileStream fs = new FileStream(path, FileMode.OpenOrCreate, FileAccess.Write);
                StreamWriter writer = new StreamWriter(fs);
                writer.Write(content);
                writer.Flush();
                writer.Close();
                fs.Close();
                MessageBox.Show("接收信息导出在:" + path);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
        }
    }
    }
